"""
Task Queue Service
任务队列服务 - 任务队列管理和状态跟踪
"""

import uuid
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_, func, update
from sqlalchemy.orm import selectinload
import structlog

from ..models.task_queue import TaskQueue, TaskType, TaskStatus, TaskPriority
from ..models.user import User
from ..core.database import DatabaseSession
from ..core.config import settings
from ..core.exceptions import (
    TaskNotFoundError,
    TaskExecutionError,
    ValidationError,
    PermissionDeniedError,
)

# 配置结构化日志
logger = structlog.get_logger(__name__)


class TaskQueueService:
    """任务队列服务类"""

    def __init__(self, db_session: DatabaseSession):
        self.db_session = db_session

    async def create_task(
        self,
        user: User,
        task_type: str,
        task_name: str,
        description: Optional[str] = None,
        priority: int = TaskPriority.NORMAL,
        task_parameters: Optional[Dict[str, Any]] = None,
        estimated_duration: Optional[int] = None,
        timeout_seconds: Optional[int] = None,
        scheduled_at: Optional[datetime] = None,
        **kwargs
    ) -> TaskQueue:
        """
        创建任务

        Args:
            user: 用户对象
            task_type: 任务类型
            task_name: 任务名称
            description: 任务描述
            priority: 任务优先级
            task_parameters: 任务参数
            estimated_duration: 预估执行时间
            timeout_seconds: 超时时间
            scheduled_at: 计划执行时间
            **kwargs: 其他参数

        Returns:
            TaskQueue: 创建的任务

        Raises:
            ValidationError: 参数验证失败
        """
        logger.info("创建任务", 
                   user_id=str(user.id), 
                   task_type=task_type, 
                   task_name=task_name)

        # 验证参数
        await self._validate_task_params(task_type, task_name, priority)

        async with self.db_session as session:
            # 检查用户并发任务限制
            await self._check_concurrent_limit(session, user.id)

            # 创建任务
            task = TaskQueue(
                user_id=user.id,
                task_type=task_type,
                task_name=task_name,
                description=description,
                task_id=str(uuid.uuid4()),
                priority=priority,
                status=TaskStatus.PENDING,
                task_parameters=task_parameters or {},
                estimated_duration=estimated_duration,
                timeout_seconds=timeout_seconds or settings.task_queue.task_timeout_seconds,
                scheduled_at=scheduled_at,
                **kwargs
            )

            session.add(task)
            await session.commit()
            await session.refresh(task)

            logger.info("任务创建成功", task_id=task.task_id, user_id=str(user.id))
            return task

    async def get_task(self, task_id: str) -> Optional[TaskQueue]:
        """
        获取任务

        Args:
            task_id: 任务ID

        Returns:
            Optional[TaskQueue]: 任务对象，不存在返回None
        """
        async with self.db_session as session:
            result = await session.execute(
                select(TaskQueue)
                .where(TaskQueue.task_id == task_id)
                .options(selectinload(TaskQueue.user))
            )
            return result.scalar_one_or_none()

    async def get_task_by_id(self, task_id: uuid.UUID) -> Optional[TaskQueue]:
        """
        根据UUID获取任务

        Args:
            task_id: 任务UUID

        Returns:
            Optional[TaskQueue]: 任务对象，不存在返回None
        """
        async with self.db_session as session:
            result = await session.execute(
                select(TaskQueue)
                .where(TaskQueue.id == task_id)
                .options(selectinload(TaskQueue.user))
            )
            return result.scalar_one_or_none()

    async def update_task_progress(
        self,
        task_id: str,
        progress: int,
        progress_message: Optional[str] = None,
        current_step: Optional[str] = None
    ) -> bool:
        """
        更新任务进度

        Args:
            task_id: 任务ID
            progress: 进度百分比 (0-100)
            progress_message: 进度消息
            current_step: 当前步骤

        Returns:
            bool: 是否成功更新
        """
        async with self.db_session as session:
            result = await session.execute(
                update(TaskQueue)
                .where(TaskQueue.task_id == task_id)
                .values(
                    progress=max(0, min(100, progress)),
                    progress_message=progress_message,
                    current_step=current_step,
                    updated_at=datetime.utcnow()
                )
            )
            await session.commit()
            return result.rowcount > 0

    async def update_task_step_progress(
        self,
        task_id: str,
        completed_steps: int,
        total_steps: Optional[int] = None,
        current_step: Optional[str] = None
    ) -> bool:
        """
        更新任务步骤进度

        Args:
            task_id: 任务ID
            completed_steps: 已完成步骤数
            total_steps: 总步骤数
            current_step: 当前步骤名称

        Returns:
            bool: 是否成功更新
        """
        async with self.db_session as session:
            # 先获取当前任务信息
            task = await self.get_task(task_id)
            if not task:
                return False

            # 更新步骤信息
            update_data = {
                "completed_steps": completed_steps,
                "updated_at": datetime.utcnow()
            }

            if total_steps is not None:
                update_data["total_steps"] = total_steps
            if current_step is not None:
                update_data["current_step"] = current_step

            # 计算进度
            if total_steps and total_steps > 0:
                update_data["progress"] = int((completed_steps / total_steps) * 100)

            result = await session.execute(
                update(TaskQueue)
                .where(TaskQueue.task_id == task_id)
                .values(**update_data)
            )
            await session.commit()
            return result.rowcount > 0

    async def mark_task_started(
        self,
        task_id: str,
        worker_id: Optional[str] = None,
        worker_type: Optional[str] = None
    ) -> bool:
        """
        标记任务开始处理

        Args:
            task_id: 任务ID
            worker_id: 工作节点ID
            worker_type: 工作节点类型

        Returns:
            bool: 是否成功更新
        """
        logger.info("任务开始处理", task_id=task_id, worker_id=worker_id)

        async with self.db_session as session:
            result = await session.execute(
                update(TaskQueue)
                .where(
                    and_(
                        TaskQueue.task_id == task_id,
                        TaskQueue.status.in_([TaskStatus.PENDING, TaskStatus.QUEUED])
                    )
                )
                .values(
                    status=TaskStatus.PROCESSING,
                    started_at=datetime.utcnow(),
                    worker_id=worker_id,
                    worker_type=worker_type,
                    progress=0,
                    updated_at=datetime.utcnow()
                )
            )
            await session.commit()
            
            success = result.rowcount > 0
            if success:
                logger.info("任务状态更新为处理中", task_id=task_id)
            return success

    async def mark_task_completed(
        self,
        task_id: str,
        result_data: Optional[Dict[str, Any]] = None,
        memory_usage_mb: Optional[int] = None,
        cpu_usage_percent: Optional[int] = None
    ) -> bool:
        """
        标记任务完成

        Args:
            task_id: 任务ID
            result_data: 结果数据
            memory_usage_mb: 内存使用（MB）
            cpu_usage_percent: CPU使用百分比

        Returns:
            bool: 是否成功更新
        """
        logger.info("任务完成", task_id=task_id)

        async with self.db_session as session:
            result = await session.execute(
                update(TaskQueue)
                .where(
                    and_(
                        TaskQueue.task_id == task_id,
                        TaskQueue.status == TaskStatus.PROCESSING
                    )
                )
                .values(
                    status=TaskStatus.COMPLETED,
                    completed_at=datetime.utcnow(),
                    progress=100,
                    result_data=result_data,
                    memory_usage_mb=memory_usage_mb,
                    cpu_usage_percent=cpu_usage_percent,
                    updated_at=datetime.utcnow()
                )
            )
            await session.commit()
            
            success = result.rowcount > 0
            if success:
                logger.info("任务状态更新为已完成", task_id=task_id)
            return success

    async def mark_task_failed(
        self,
        task_id: str,
        error_message: str,
        error_code: Optional[str] = None,
        error_details: Optional[Dict[str, Any]] = None
    ) -> bool:
        """
        标记任务失败

        Args:
            task_id: 任务ID
            error_message: 错误消息
            error_code: 错误代码
            error_details: 错误详情

        Returns:
            bool: 是否成功更新
        """
        logger.error("任务失败", task_id=task_id, error_message=error_message, error_code=error_code)

        async with self.db_session as session:
            result = await session.execute(
                update(TaskQueue)
                .where(
                    and_(
                        TaskQueue.task_id == task_id,
                        TaskQueue.status == TaskStatus.PROCESSING
                    )
                )
                .values(
                    status=TaskStatus.FAILED,
                    completed_at=datetime.utcnow(),
                    error_message=error_message,
                    error_code=error_code,
                    error_details=error_details,
                    updated_at=datetime.utcnow()
                )
            )
            await session.commit()
            return result.rowcount > 0

    async def mark_task_cancelled(self, task_id: str) -> bool:
        """
        标记任务已取消

        Args:
            task_id: 任务ID

        Returns:
            bool: 是否成功更新
        """
        logger.info("任务取消", task_id=task_id)

        async with self.db_session as session:
            result = await session.execute(
                update(TaskQueue)
                .where(
                    and_(
                        TaskQueue.task_id == task_id,
                        TaskQueue.status.in_([TaskStatus.PENDING, TaskStatus.QUEUED])
                    )
                )
                .values(
                    status=TaskStatus.CANCELLED,
                    completed_at=datetime.utcnow(),
                    updated_at=datetime.utcnow()
                )
            )
            await session.commit()
            return result.rowcount > 0

    async def get_pending_tasks(
        self,
        task_type: Optional[str] = None,
        limit: int = 100,
        user_id: Optional[uuid.UUID] = None
    ) -> List[TaskQueue]:
        """
        获取待处理任务

        Args:
            task_type: 任务类型过滤
            limit: 限制数量
            user_id: 用户ID过滤

        Returns:
            List[TaskQueue]: 待处理任务列表
        """
        async with self.db_session as session:
            query = select(TaskQueue).where(TaskQueue.status == TaskStatus.PENDING)

            # 应用过滤条件
            if task_type:
                query = query.where(TaskQueue.task_type == task_type)
            if user_id:
                query = query.where(TaskQueue.user_id == user_id)

            # 按优先级排序
            query = query.order_by(TaskQueue.priority.desc(), TaskQueue.created_at.asc()).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def get_processing_tasks(
        self,
        worker_id: Optional[str] = None,
        limit: int = 100
    ) -> List[TaskQueue]:
        """
        获取处理中任务

        Args:
            worker_id: 工作节点ID过滤
            limit: 限制数量

        Returns:
            List[TaskQueue]: 处理中任务列表
        """
        async with self.db_session as session:
            query = select(TaskQueue).where(TaskQueue.status == TaskStatus.PROCESSING)

            if worker_id:
                query = query.where(TaskQueue.worker_id == worker_id)

            query = query.limit(limit)
            result = await session.execute(query)
            return result.scalars().all()

    async def get_user_tasks(
        self,
        user: User,
        status: Optional[str] = None,
        task_type: Optional[str] = None,
        skip: int = 0,
        limit: int = 50
    ) -> List[TaskQueue]:
        """
        获取用户的任务列表

        Args:
            user: 用户对象
            status: 状态过滤
            task_type: 任务类型过滤
            skip: 跳过数量
            limit: 限制数量

        Returns:
            List[TaskQueue]: 任务列表
        """
        async with self.db_session as session:
            query = select(TaskQueue).where(TaskQueue.user_id == user.id)

            # 应用过滤条件
            if status:
                query = query.where(TaskQueue.status == status)
            if task_type:
                query = query.where(TaskQueue.task_type == task_type)

            # 排序和分页
            query = query.order_by(TaskQueue.created_at.desc()).offset(skip).limit(limit)

            result = await session.execute(query)
            return result.scalars().all()

    async def retry_task(self, task_id: str, user: User) -> Optional[TaskQueue]:
        """
        重试任务

        Args:
            task_id: 任务ID
            user: 用户对象

        Returns:
            Optional[TaskQueue]: 重试后的任务对象

        Raises:
            TaskNotFoundError: 任务不存在
            PermissionDeniedError: 无权操作
            TaskExecutionError: 任务无法重试
        """
        logger.info("重试任务", task_id=task_id, user_id=str(user.id))

        async with self.db_session as session:
            task = await self.get_task(task_id)
            if not task:
                raise TaskNotFoundError(f"任务 {task_id} 不存在")

            # 检查权限
            if task.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权重试此任务")

            # 检查是否可以重试
            if not task.can_retry:
                raise TaskExecutionError(f"当前状态为 {task.status}，无法重试")

            # 重置任务状态
            task.status = TaskStatus.PENDING
            task.progress = 0
            task.error_message = None
            task.error_code = None
            task.error_details = None
            task.retry_count = task.retry_count + 1
            task.started_at = None
            task.completed_at = None
            task.worker_id = None
            task.worker_type = None

            await session.commit()
            await session.refresh(task)

            logger.info("任务重试成功", task_id=task_id)
            return task

    async def cancel_task(self, task_id: str, user: User) -> bool:
        """
        取消任务

        Args:
            task_id: 任务ID
            user: 用户对象

        Returns:
            bool: 是否成功取消

        Raises:
            TaskNotFoundError: 任务不存在
            PermissionDeniedError: 无权操作
        """
        logger.info("取消任务", task_id=task_id, user_id=str(user.id))

        async with self.db_session as session:
            task = await self.get_task(task_id)
            if not task:
                raise TaskNotFoundError(f"任务 {task_id} 不存在")

            # 检查权限
            if task.user_id != user.id and not user.is_admin:
                raise PermissionDeniedError("无权取消此任务")

            # 检查是否可以取消
            if task.status not in [TaskStatus.PENDING, TaskStatus.QUEUED]:
                return False

            # 更新任务状态
            success = await self.mark_task_cancelled(task_id)
            await session.commit()

            if success:
                logger.info("任务取消成功", task_id=task_id)
            return success

    async def get_task_statistics(self, user: Optional[User] = None) -> Dict[str, Any]:
        """
        获取任务统计信息

        Args:
            user: 用户对象，如果为None则获取系统统计

        Returns:
            Dict[str, Any]: 统计信息
        """
        async with self.db_session as session:
            base_query = select(TaskQueue)
            if user:
                base_query = base_query.where(TaskQueue.user_id == user.id)

            # 总体统计
            total_result = await session.execute(
                select(
                    func.count(TaskQueue.id),
                    func.count(TaskQueue.id).filter(TaskQueue.status == TaskStatus.COMPLETED),
                    func.count(TaskQueue.id).filter(TaskQueue.status == TaskStatus.FAILED),
                    func.count(TaskQueue.id).filter(TaskQueue.status == TaskStatus.PROCESSING),
                    func.count(TaskQueue.id).filter(TaskQueue.status == TaskStatus.PENDING)
                )
            )
            total, completed, failed, processing, pending = total_result.first()

            # 按任务类型统计
            by_type_result = await session.execute(
                select(
                    TaskQueue.task_type,
                    func.count(TaskQueue.id)
                ).group_by(TaskQueue.task_type)
            )
            by_type = dict(by_type_result.all())

            # 最近完成的任务
            recent_completed_result = await session.execute(
                select(TaskQueue)
                .where(TaskQueue.status == TaskStatus.COMPLETED)
                .order_by(TaskQueue.completed_at.desc())
                .limit(10)
            )
            recent_completed = recent_completed_result.scalars().all()

            return {
                "total_tasks": total or 0,
                "completed_tasks": completed or 0,
                "failed_tasks": failed or 0,
                "processing_tasks": processing or 0,
                "pending_tasks": pending or 0,
                "success_rate": (completed / total * 100) if total > 0 else 0,
                "by_task_type": by_type,
                "recent_completed": [
                    {
                        "task_id": task.task_id,
                        "task_name": task.task_name,
                        "task_type": task.task_type,
                        "completed_at": task.completed_at.isoformat() if task.completed_at else None,
                        "processing_time": task.processing_time_seconds
                    }
                    for task in recent_completed
                ]
            }

    async def cleanup_expired_tasks(self, max_age_days: int = 30) -> int:
        """
        清理过期任务

        Args:
            max_age_days: 最大保留天数

        Returns:
            int: 清理的任务数量
        """
        cutoff_date = datetime.utcnow() - timedelta(days=max_age_days)

        async with self.db_session as session:
            result = await session.execute(
                select(TaskQueue)
                .where(
                    and_(
                        TaskQueue.status.in_([TaskStatus.COMPLETED, TaskStatus.FAILED, TaskStatus.CANCELLED]),
                        TaskQueue.completed_at < cutoff_date
                    )
                )
            )
            expired_tasks = result.scalars().all()

            # 删除过期任务
            for task in expired_tasks:
                await session.delete(task)

            await session.commit()

            cleaned_count = len(expired_tasks)
            logger.info("清理过期任务完成", cleaned_count=cleaned_count, max_age_days=max_age_days)
            return cleaned_count

    async def get_next_pending_task(
        self,
        worker_id: str,
        worker_type: str,
        task_types: Optional[List[str]] = None
    ) -> Optional[TaskQueue]:
        """
        获取下一个待处理任务（工作节点调用）

        Args:
            worker_id: 工作节点ID
            worker_type: 工作节点类型
            task_types: 支持的任务类型

        Returns:
            Optional[TaskQueue]: 任务对象，没有返回None
        """
        async with self.db_session as session:
            query = select(TaskQueue).where(TaskQueue.status == TaskStatus.PENDING)

            # 任务类型过滤
            if task_types:
                query = query.where(TaskQueue.task_type.in_(task_types))

            # 检查依赖关系
            # 只获取依赖任务都已完成的任务
            query = query.where(
                or_(
                    TaskQueue.depends_on_task_ids == None,
                    TaskQueue.depends_on_task_ids == []
                )
            )

            # 按优先级和时间排序
            query = query.order_by(
                TaskQueue.priority.desc(),
                TaskQueue.created_at.asc()
            ).limit(1)

            result = await session.execute(query)
            task = result.scalar_one_or_none()

            if task:
                # 标记为已排队
                task.status = TaskStatus.QUEUED
                task.queued_at = datetime.utcnow()
                await session.commit()

                logger.info("获取待处理任务", 
                           task_id=task.task_id, 
                           worker_id=worker_id,
                           task_type=task.task_type)

            return task

    # 私有辅助方法

    async def _validate_task_params(
        self,
        task_type: str,
        task_name: str,
        priority: int
    ) -> None:
        """验证任务参数"""
        if not task_type or len(task_type.strip()) == 0:
            raise ValidationError("任务类型不能为空")
        
        if not task_name or len(task_name.strip()) == 0:
            raise ValidationError("任务名称不能为空")
        
        if len(task_name) > 200:
            raise ValidationError("任务名称过长，最大200字符")
        
        if priority < TaskPriority.LOWEST or priority > TaskPriority.URGENT:
            raise ValidationError(f"任务优先级必须在 {TaskPriority.LOWEST}-{TaskPriority.URGENT} 之间")

    async def _check_concurrent_limit(self, session: AsyncSession, user_id: uuid.UUID) -> None:
        """检查并发任务限制"""
        # 检查用户并发限制
        result = await session.execute(
            select(func.count(TaskQueue.id))
            .where(
                and_(
                    TaskQueue.user_id == user_id,
                    TaskQueue.status.in_([TaskStatus.PENDING, TaskStatus.QUEUED, TaskStatus.PROCESSING])
                )
            )
        )
        user_concurrent = result.scalar()

        if user_concurrent >= settings.user_limit.max_concurrent_tasks_per_user:
            raise ValidationError(f"用户并发任务数已达上限: {settings.user_limit.max_concurrent_tasks_per_user}")

        # 检查系统并发限制
        result = await session.execute(
            select(func.count(TaskQueue.id))
            .where(TaskQueue.status.in_([TaskStatus.PENDING, TaskStatus.QUEUED, TaskStatus.PROCESSING]))
        )
        system_concurrent = result.scalar()

        if system_concurrent >= settings.user_limit.max_concurrent_tasks_system:
            raise ValidationError(f"系统并发任务数已达上限: {settings.user_limit.max_concurrent_tasks_system}")

    async def _check_task_dependencies(self, session: AsyncSession, task: TaskQueue) -> bool:
        """检查任务依赖是否满足"""
        if not task.depends_on_task_ids:
            return True

        # 检查所有依赖任务是否已完成
        result = await session.execute(
            select(func.count(TaskQueue.id))
            .where(
                and_(
                    TaskQueue.task_id.in_(task.depends_on_task_ids),
                    TaskQueue.status == TaskStatus.COMPLETED
                )
            )
        )
        completed_count = result.scalar()

        return completed_count == len(task.depends_on_task_ids)